﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using BestHTTP;
using BestHTTP.Logger;
using Application = UnityEngine.Application;

namespace Networking.Runtime
{
    public class HttpFileDownLoaders : XNetObject
    {
        private ConcurrentQueue<DownLoadFileData> _downLoadItems = new ConcurrentQueue<DownLoadFileData>();
        
        private LinkedList<HttpFileDownLoadRequest> _activeRequests = new LinkedList<HttpFileDownLoadRequest>();
        public int TotalLoadCount { get; private set; }
        public int LoadedCount { get; private set; }
        public int TotalLoadSize { get; private set; }

        public bool IsAllLoadFinish => LoadedCount == TotalLoadCount;

        public HttpFileDownLoaders()
        {
            Application.targetFrameRate = 60;
            Application.runInBackground = true;
            DownLoadSetting.UseThreadUpdate = true;
            HTTPManager.Logger.Level = Loglevels.Error;
            HTTPManager.IsCachingDisabled = true;
            HTTPManager.RequestTimeout = TimeSpan.FromSeconds(DownLoadSetting.DOWNLOAD_TIME_OUT);
            HTTPManager.ConnectTimeout = TimeSpan.FromSeconds(DownLoadSetting.CONNECT_TIMEOUT);
            HTTPManager.Setup();
            HTTPUpdateDelegator.OnApplicationForegroundStateChanged += OnApplicationForegroundStateChanged;
        }

        private void OnApplicationForegroundStateChanged(bool isForeground)
        {
            if (isForeground)
            {
                DownLoadSetting.StopTask();
            }
            else
            {
                DownLoadSetting.StartTask();
            }
        }

        public void AddDownLoad(DownLoadFileData loadItem)
        {
            if (loadItem.IsValid())
            {
                if (loadItem.IsFileDownloaded())
                {
                    LoadedCount += 1;
                }
                else
                {
                    _downLoadItems.Enqueue(loadItem);
                }
                TotalLoadCount += 1;
                TotalLoadSize += TotalLoadCount;
            }
        }
        
        public void StartDownLoad()
        {
            while (_downLoadItems.Count > 0 && _activeRequests.Count < DownLoadSetting.DOWN_LOAD_THREAD_COUNT)
            {
                HttpFileDownLoadRequest loadRequest = new HttpFileDownLoadRequest();
                RecycleDownload(loadRequest);
            }
        }

        private void RecycleDownload(HttpFileDownLoadRequest loadRequest)
        {
            if (_downLoadItems.Count == 0)
            {
                return;
            }

            if (_downLoadItems.TryDequeue( out var loadItem))
            {
                _activeRequests.AddLast(loadRequest);
                loadRequest.OnDownLoadFinish = ProcessOnDownLoadFinish;
                loadRequest.BeginDownload(loadItem); 
            }
        }
        
        private void ProcessOnDownLoadFinish(HttpFileDownLoadRequest request)
        {
            lock (_activeRequests)
            {
                using (request)
                {
                    if (_activeRequests.Remove(request))
                    {
                        if (request.IsSuccess)
                        {
                            LoadedCount++;
                        }
                        else
                        {
                            _downLoadItems.Enqueue(request.DownLoadFileData);
                        }
                        request.Dispose();
                        RecycleDownload(new HttpFileDownLoadRequest());
                    } 
                }
            }
        }
        
        protected override void OnDispose()
        {
            var current = _activeRequests.Last;
            while (current!= null)
            {
                var previous = current;
                current = previous.Previous;
                _activeRequests.Remove(previous);
                previous.Value.Dispose();
            }
            _downLoadItems.Clear();
            HTTPUpdateDelegator.OnApplicationForegroundStateChanged -= OnApplicationForegroundStateChanged;
        }
    }
}